﻿using System;
using System.Collections.Generic;
using l1cs.server.datatables;
using l1cs.server.model.Instance;
using l1cs.server.model.skill;
using l1cs.server.serverpackets;

namespace l1cs.server.model
{
    class L1PolyMorph
    {
        private static Logger _log = Logger.getLogger( typeof( L1PolyMorph ).FullName );

        // weapon equip bit
        private const int DAGGER_EQUIP = 1;

        private const int SWORD_EQUIP = 2;

        private const int TWOHANDSWORD_EQUIP = 4;

        private const int AXE_EQUIP = 8;

        private const int SPEAR_EQUIP = 16;

        private const int STAFF_EQUIP = 32;

        private const int EDORYU_EQUIP = 64;

        private const int CLAW_EQUIP = 128;

        private const int BOW_EQUIP = 256; // ガントレット含む

        private const int KIRINGKU_EQUIP = 512;

        private const int CHAINSWORD_EQUIP = 1024;

        // armor equip bit
        private const int HELM_EQUIP = 1;

        private const int AMULET_EQUIP = 2;

        private const int EARRING_EQUIP = 4;

        private const int TSHIRT_EQUIP = 8;

        private const int ARMOR_EQUIP = 16;

        private const int CLOAK_EQUIP = 32;

        private const int BELT_EQUIP = 64;

        private const int SHIELD_EQUIP = 128;

        private const int GLOVE_EQUIP = 256;

        private const int RING_EQUIP = 512;

        private const int BOOTS_EQUIP = 1024;

        private const int GUARDER_EQUIP = 2048;

        // 変身の原因を示すbit
        public static int MORPH_BY_ITEMMAGIC = 1;

        public static int MORPH_BY_GM = 2;

        public static int MORPH_BY_NPC = 4; // 占星術師ケプリシャ以外のNPC

        public static int MORPH_BY_KEPLISHA = 8;

        public static int MORPH_BY_LOGIN = 0;

        private static Dictionary<int, int> weaponFlgMap = new Dictionary<int, int>();
        private static Dictionary<int, int> armorFlgMap = new Dictionary<int, int>();
        static L1PolyMorph()
        {
            weaponFlgMap[1] = SWORD_EQUIP;
            weaponFlgMap[2] = DAGGER_EQUIP;
            weaponFlgMap[3] = TWOHANDSWORD_EQUIP;
            weaponFlgMap[4] = BOW_EQUIP;
            weaponFlgMap[5] = SPEAR_EQUIP;
            weaponFlgMap[6] = AXE_EQUIP;
            weaponFlgMap[7] = STAFF_EQUIP;
            weaponFlgMap[8] = BOW_EQUIP;
            weaponFlgMap[9] = BOW_EQUIP;
            weaponFlgMap[10] = BOW_EQUIP;
            weaponFlgMap[11] = CLAW_EQUIP;
            weaponFlgMap[12] = EDORYU_EQUIP;
            weaponFlgMap[13] = BOW_EQUIP;
            weaponFlgMap[14] = SPEAR_EQUIP;
            weaponFlgMap[15] = AXE_EQUIP;
            weaponFlgMap[16] = STAFF_EQUIP;
            weaponFlgMap[17] = KIRINGKU_EQUIP;
            weaponFlgMap[18] = CHAINSWORD_EQUIP;

            armorFlgMap[1] = HELM_EQUIP;
            armorFlgMap[2] = ARMOR_EQUIP;
            armorFlgMap[3] = TSHIRT_EQUIP;
            armorFlgMap[4] = CLOAK_EQUIP;
            armorFlgMap[5] = GLOVE_EQUIP;
            armorFlgMap[6] = BOOTS_EQUIP;
            armorFlgMap[7] = SHIELD_EQUIP;
            armorFlgMap[8] = AMULET_EQUIP;
            armorFlgMap[9] = RING_EQUIP;
            armorFlgMap[10] = BELT_EQUIP;
            armorFlgMap[12] = EARRING_EQUIP;
            armorFlgMap[13] = GUARDER_EQUIP;
        }

        private int _id;
        private String _name;
        private int _polyId;
        private int _minLevel;
        private int _weaponEquipFlg;
        private int _armorEquipFlg;
        private bool _canUseSkill;
        private int _causeFlg;

        public L1PolyMorph( int id, String name, int polyId, int minLevel, int weaponEquipFlg, int armorEquipFlg, bool canUseSkill, int causeFlg )
        {
            _id = id;
            _name = name;
            _polyId = polyId;
            _minLevel = minLevel;
            _weaponEquipFlg = weaponEquipFlg;
            _armorEquipFlg = armorEquipFlg;
            _canUseSkill = canUseSkill;
            _causeFlg = causeFlg;
        }

        public int getId()
        {
            return _id;
        }

        public String getName()
        {
            return _name;
        }

        public int getPolyId()
        {
            return _polyId;
        }

        public int getMinLevel()
        {
            return _minLevel;
        }

        public int getWeaponEquipFlg()
        {
            return _weaponEquipFlg;
        }

        public int getArmorEquipFlg()
        {
            return _armorEquipFlg;
        }

        public bool canUseSkill()
        {
            return _canUseSkill;
        }

        public int getCauseFlg()
        {
            return _causeFlg;
        }

        public static void handleCommands( L1PcInstance pc, String s )
        {
            if ( pc == null || pc.isDead() )
            {
                return;
            }
            L1PolyMorph poly = PolyTable.getInstance().getTemplate( s );
            if ( poly != null || s.Equals( "none" ) )
            {
                if ( s.Equals( "none" ) )
                {
                    if ( pc.getTempCharGfx() == 6034
                            || pc.getTempCharGfx() == 6035 )
                    {
                    }
                    else
                    {
                        pc.removeSkillEffect( L1SkillId.SHAPE_CHANGE );
                        pc.sendPackets( new S_CloseList( pc.getId() ) );
                    }
                }
                else if ( pc.getLevel() >= poly.getMinLevel() || pc.isGm() )
                {
                    if ( pc.getTempCharGfx() == 6034
                            || pc.getTempCharGfx() == 6035 )
                    {
                        pc.sendPackets( new S_ServerMessage( 181 ) );
                        // \f1そのようなモンスターには変身できません。
                    }
                    else
                    {
                        doPoly( pc, poly.getPolyId(), 7200, MORPH_BY_ITEMMAGIC );
                        pc.sendPackets( new S_CloseList( pc.getId() ) );
                    }
                }
                else
                {
                    pc.sendPackets( new S_ServerMessage( 181 ) ); // \f1そのようなモンスターには変身できません。
                }
            }
        }

        public static void doPoly( L1Character cha, int polyId, int timeSecs, int cause )
        {
            if ( cha == null || cha.isDead() )
            {
                return;
            }
            if ( cha is L1PcInstance )
            {
                L1PcInstance pc = (L1PcInstance)cha;
                if ( pc.getMapId() == 5124 )
                { // 釣り場
                    pc.sendPackets( new S_ServerMessage( 1170 ) ); // ここでは変身できません。
                    return;
                }
                if ( pc.getTempCharGfx() == 6034
                        || pc.getTempCharGfx() == 6035 )
                {
                    pc.sendPackets( new S_ServerMessage( 181 ) ); // \f1そのようなモンスターには変身できません。
                    return;
                }
                if ( !isMatchCause( polyId, cause ) )
                {
                    pc.sendPackets( new S_ServerMessage( 181 ) ); // \f1そのようなモンスターには変身できません。
                    return;
                }

                pc.killSkillEffectTimer( L1SkillId.SHAPE_CHANGE );
                pc.setSkillEffect( L1SkillId.SHAPE_CHANGE, timeSecs * 1000 );
                if ( pc.getTempCharGfx() != polyId )
                { // 同じ変身の場合はアイコン送信以外が必要ない
                    L1ItemInstance weapon = pc.getWeapon();
                    // 変身によって武器が外れるか
                    bool weaponTakeoff = ( weapon != null && !isEquipableWeapon( polyId, weapon.getItem().getType() ) );
                    pc.setTempCharGfx( polyId );
                    pc.sendPackets( new S_ChangeShape( pc.getId(), polyId, weaponTakeoff ) );
                    if ( !pc.isGmInvis() && !pc.isInvisble() )
                    {
                        pc.broadcastPacket( new S_ChangeShape( pc.getId(), polyId ) );
                    }
                    if ( pc.isGmInvis() )
                    {
                    }
                    else if ( pc.isInvisble() )
                    {
                        pc.broadcastPacketForFindInvis( new S_ChangeShape( pc.getId(), polyId ), true );
                    }
                    else
                    {
                        pc.broadcastPacket( new S_ChangeShape( pc.getId(), polyId ) );
                    }
                    pc.getInventory().takeoffEquip( polyId );
                    weapon = pc.getWeapon();
                    if ( weapon != null )
                    {
                        S_CharVisualUpdate charVisual = new S_CharVisualUpdate( pc );
                        pc.sendPackets( charVisual );
                        pc.broadcastPacket( charVisual );
                    }
                }
                pc.sendPackets( new S_SkillIconGFX( 35, timeSecs ) );
            }
            else if ( cha is L1MonsterInstance )
            {
                L1MonsterInstance mob = (L1MonsterInstance)cha;
                mob.killSkillEffectTimer( L1SkillId.SHAPE_CHANGE );
                mob.setSkillEffect( L1SkillId.SHAPE_CHANGE, timeSecs * 1000 );
                if ( mob.getTempCharGfx() != polyId )
                { // 同じ変身の場合はアイコン送信以外が必要ない
                    mob.setTempCharGfx( polyId );
                    mob.broadcastPacket( new S_ChangeShape( mob.getId(), polyId ) );
                }
            }
        }

        public static void undoPoly( L1Character cha )
        {
            if ( cha is L1PcInstance )
            {
                L1PcInstance pc = (L1PcInstance)cha;
                int classId = pc.getClassId();
                pc.setTempCharGfx( classId );
                pc.sendPackets( new S_ChangeShape( pc.getId(), classId ) );
                pc.broadcastPacket( new S_ChangeShape( pc.getId(), classId ) );
                L1ItemInstance weapon = pc.getWeapon();
                if ( weapon != null )
                {
                    S_CharVisualUpdate charVisual = new S_CharVisualUpdate( pc );
                    pc.sendPackets( charVisual );
                    pc.broadcastPacket( charVisual );
                }
            }
            else if ( cha is L1MonsterInstance )
            {
                L1MonsterInstance mob = (L1MonsterInstance)cha;
                mob.setTempCharGfx( 0 );
                mob.broadcastPacket( new S_ChangeShape( mob.getId(), mob.getGfxId() ) );
            }
        }

        /// <summary>
        /// 指定したpolyIdがweapontTypeの武器を装備出来るか？
        /// </summary>
        /// <param name="polyId"></param>
        /// <param name="weaponType"></param>
        /// <returns></returns>
        public static bool isEquipableWeapon( int polyId, int weaponType )
        {
            L1PolyMorph poly = PolyTable.getInstance().getTemplate( polyId );
            if ( poly == null )
            {
                return true;
            }

            if ( weaponFlgMap.ContainsKey( weaponType ) )
            {
                int flg = weaponFlgMap[weaponType];
                return 0 != ( poly.getWeaponEquipFlg() & flg );
            }
            return true;
        }

        /// <summary>
        /// 指定したpolyIdがarmorTypeの防具を装備出来るか？
        /// </summary>
        /// <param name="polyId"></param>
        /// <param name="armorType"></param>
        /// <returns></returns>
        public static bool isEquipableArmor( int polyId, int armorType )
        {
            L1PolyMorph poly = PolyTable.getInstance().getTemplate( polyId );
            if ( poly == null )
            {
                return true;
            }

            if ( armorFlgMap.ContainsKey( armorType ) )
            {
                int flg = armorFlgMap[armorType];
                return 0 != ( poly.getArmorEquipFlg() & flg );
            }
            return true;
        }

        /// <summary>
        /// 指定したpolyIdが何によって変身し、それが変身させられるか？
        /// </summary>
        /// <param name="polyId"></param>
        /// <param name="cause"></param>
        /// <returns></returns>
        public static bool isMatchCause( int polyId, int cause )
        {
            L1PolyMorph poly = PolyTable.getInstance().getTemplate( polyId );
            if ( poly == null )
            {
                return true;
            }
            if ( cause == MORPH_BY_LOGIN )
            {
                return true;
            }

            return 0 != ( poly.getCauseFlg() & cause );
        }
    }
}
